/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Copyright 2002-2003 by Mark Wolgemuth, All rights reserved.             */
/* Copyright 2002-2003 by Employease, Inc.  All rights reserved.           */
/*                                                                         */
/* Redistribution and use in source and binary forms, with or without      */
/* modification, are permitted provided that the following conditions      */
/* are met:                                                                */
/*                                                                         */
/*    1. Redistributions of source code must retain the above copyright    */
/* notice, this list of conditions and the following disclaimer.           */
/*    2. Redistributions in binary form must reproduce the above copyright */
/* notice, this list of conditions and the following disclaimer in the     */
/* documentation and/or other materials provided with the distribution.    */
/*                                                                         */
/* THIS SOFTWARE IS PROVIDED BY THE MOD_ATHENA PROJECT ``AS IS'' AND ANY   */
/* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE       */
/* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR      */
/* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR        */
/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,   */
/* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,     */
/* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR      */
/* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY     */
/* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT            */
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE   */
/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.    */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "ath_lb.h"
#include "apr_hash.h"

/* name string to obj hash cache */
static apr_hash_t *_cache = NULL;
static apr_pool_t *_cache_ppool = NULL;
static apr_pool_t *_cache_pool = NULL;

/* internal struct */
typedef struct ath_farm_cache ath_farm_cache;
struct ath_farm_cache {
    ath_farm *farm;
};

/* alloc farm obj, used in mirroring funcs */
ath_farm * 
ath_farm_alloc(apr_pool_t *pool)
{
    ath_farm *farm = (ath_farm *) apr_pcalloc(pool, sizeof(ath_farm));
    ASSERT(farm);
    ath_farm_format(farm);
    return farm;
}

/* allocate a temporary farm config object 
 * used by ath_conf_farm as containers prior to shm init
 */
ath_cfg_farm * 
ath_farm_cfg_alloc(apr_pool_t *pool)
{
    ath_cfg_farm *farm = (ath_cfg_farm *) 
      apr_pcalloc(pool, sizeof(ath_cfg_farm));
    ASSERT(farm);
    farm->on = -1;
    farm->name = NULL;
    farm->down_url = NULL;
    farm->offline_url = NULL;
    farm->members = ath_list_ap_alloc(pool);
    farm->algo_str = NULL;
    farm->expect_update = ATH_EOA;
    farm->hit_adds_to = ATH_ALGO_NON;
    farm->hit_adds = 0;
    farm->smartfarmable = ATH_ON; /* not checked unless global smartfarming is on */
    farm->max_excluded = ATH_EOA;
    return farm;
}

/* allocate a temporary farm update object
 * used by ath_engine_update_farm
 */
ath_upd_farm *
ath_farm_upd_alloc(apr_pool_t *pool)
{
    ath_upd_farm *farm_up;
    farm_up = (ath_upd_farm *) apr_pcalloc(pool, sizeof(ath_upd_farm));
    farm_up->farms = ath_list_ap_alloc(pool);
    farm_up->on_u = 0;
    farm_up->member_add = ath_list_ap_alloc(pool);
    farm_up->member_rem = ath_list_ap_alloc(pool);
    farm_up->str_algo = NULL;
    farm_up->hit_adds_u = 0;
    farm_up->hit_adds_to = ATH_ALGO_NON;
    farm_up->expect_u = 0;
    farm_up->ttl_u = 0;
    farm_up->rttl_u = 0;
    farm_up->str_down_url = NULL;
    farm_up->str_offline_url = NULL;
    farm_up->smartfarmable_u = 0;
    return farm_up;
}

void
ath_farm_cache_init(apr_pool_t *pool)
{
    apr_pool_t *subpool;
    ASSERT(pool);
    /* cleanup first */
    if (_cache)
	ath_farm_cache_expire();
    /* always use the same parent pool we started with */
    if (_cache_ppool) 
	ASSERT(_cache_ppool == pool);
    else
	_cache_ppool = pool;
    apr_pool_create(&subpool, _cache_ppool);
    _cache_pool = subpool;
    _cache = apr_hash_make(_cache_pool);
}

void 
ath_farm_cache_expire()
{
    if (_cache)
      {
	  ASSERT(_cache_pool);
	  _cache = NULL;
	  apr_pool_destroy(_cache_pool);
	  _cache_pool = NULL;
      }
}

/* formats shm chunck for single farm */
void 
ath_farm_format(ath_farm *farm)
{
    int j;
    /* write NULL into all name / URL fields */
    for (j = 0; j < ATH_NAME_MAX; j++)
      {
	  farm->name[j] = (int) NULL;
	  farm->down_url[j] = (int) NULL;
	  farm->offline_url[j] = (int) NULL;
      }
    /* initialize / terminate our member array */
    for (j = 0; j < ATH_PHYS_HARDMAX + 1; j++)
      {
	  farm->member_phys_idx[j] = ATH_EOA;
      }
    farm->hit_adds = 0;
    farm->hit_adds_to = ATH_ALGO_NON;
    farm->expect_update = 0;
    farm->member_chosen_idx = ATH_EOA;
    farm->exists = 0;
    farm->on = 0;
    farm->allow_ttl = 0;
    farm->smartfarmable = 0;
    /* no lock assigned yet */
    farm->lock_idx = ATH_EOA;
}

/* copys one farm obj to another, used in mirror */
ath_farm * 
ath_farm_copy(ath_farm *farm_dst, const ath_farm *farm_src)
{
    int j;

    /* expire cache if name is changing */
    if ((strlen(farm_dst->name) != strlen(farm_src->name))
	|| (strcmp(farm_dst->name, farm_src->name)))
	ath_farm_cache_expire();

    for (j = 0; j < ATH_NAME_MAX; j++)
      {
	  farm_dst->name[j] = farm_src->name[j];
	  farm_dst->down_url[j] = farm_src->down_url[j];
	  farm_dst->offline_url[j] = farm_src->offline_url[j];
      }
    for (j = 0; j < ATH_PHYS_HARDMAX + 1; j++)
	farm_dst->member_phys_idx[j] = farm_src->member_phys_idx[j];
    ath_algo_def_copy(farm_dst->algo_def, farm_src->algo_def); 
    farm_dst->hit_adds = farm_src->hit_adds;
    farm_dst->hit_adds_to = farm_src->hit_adds_to;
    farm_dst->expect_update = farm_src->expect_update;
    farm_dst->member_chosen_idx = farm_src->member_chosen_idx;
    farm_dst->exists = farm_src->exists;
    farm_dst->on = farm_src->on;
    farm_dst->allow_ttl = farm_src->allow_ttl;
    farm_dst->smartfarmable = farm_src->smartfarmable;
    /* DO NOT COPY LOCK IDX 
     * if this is a real shm farm, it was given a lock_idx on 
     * startup if dst is alloced, then lock not needed or must
     * be manually set
     */
    /*  farm_dst->lock_idx = farm_src->lock_idx; */
    return farm_dst;
}

/* configures a real shm farm from a farm_cfg object */
ath_farm * 
ath_farm_configure(ath_farm *farm, ath_cfg_farm *f_cfg, int l_idx)
{
    ath_engine *engine = ath_engine_get();
    int i, j, idx, fidx;
    ath_cfg_phys *phys_cfg;
    ath_phys *phys;
    ath_node *listctx;
    ASSERT(farm);
    ASSERT(f_cfg);
    ASSERT(l_idx > -1);

    fidx = farm->idx;
    /* set lock */
    farm->lock_idx = l_idx; 
    farm->exists = 1;
    /* set name */
    ath_shm_strncpy(farm->name, f_cfg->name, ATH_NAME_MAX);
    /* set down/offline paths */
    if (f_cfg->down_url != NULL)
	ath_shm_strncpy(farm->down_url, f_cfg->down_url, ATH_NAME_MAX);
    else if (engine->down_url)
	ath_shm_strncpy(farm->down_url, engine->down_url, ATH_NAME_MAX);
    if (f_cfg->offline_url != NULL)
	ath_shm_strncpy(farm->offline_url, f_cfg->offline_url, ATH_NAME_MAX);
    else if (engine->offline_url)
	ath_shm_strncpy(farm->offline_url, engine->offline_url, ATH_NAME_MAX);
    /* set members */
    ath_list_rewind_r(f_cfg->members, &listctx);
    i = 0;
    while (ath_list_hasnext_r(f_cfg->members, &listctx))
      {
	  phys_cfg = (ath_cfg_phys *) 
	    ath_list_getnext_r(f_cfg->members, &listctx);
	  phys = ath_phys_for_host(phys_cfg->host);
	  if (phys)
	    {
		farm->member_phys_idx[i] = phys->idx;
		/* we need to gaurantee that this is set to a member at start */
		/* for nominate on update */
		farm->member_chosen_idx = i;
		/* set farm membership in phys also */
		j = 0;
		while (phys->membership_idx[j] != fidx)
		  {
		      /* find an open spot */
		      if ((phys->membership_idx[j] == ATH_EOA)
			  || phys->membership_idx[j] == ATH_OPN)
			  break;
		      j++;
		  }
		if (phys->membership_idx[j] != fidx)
		    phys->membership_idx[j] = fidx;
		i++;
	    }
      }
    /* first copy defaults from engine */
    ath_algo_def_copy(farm->algo_def, engine->algo_def);
    /* is it on at startup */
    if (f_cfg->on > -1)
	farm->on = f_cfg->on;
    else
	farm->on = engine->default_farm_on;
    if (engine->allow_ttl != 0)
	farm->allow_ttl = engine->allow_ttl;
    if (engine->recover_ttl != 0)
	farm->recover_ttl = engine->recover_ttl;
    if (engine->expect_update == 1)
	farm->expect_update = 1;
    if (engine->hit_adds_to != ATH_ALGO_NON)
      {
	  farm->hit_adds_to = engine->hit_adds_to;
	  farm->hit_adds = engine->hit_adds;
      }
    /* next get our confs */
    if (f_cfg->algo_str != NULL)
	ath_algo_def_from_str(farm->algo_def, f_cfg->algo_str);
    if (f_cfg->hit_adds_to != (int) NULL)
      {
	  farm->hit_adds_to = f_cfg->hit_adds_to;
	  farm->hit_adds = f_cfg->hit_adds;
      }
    if (f_cfg->max_excluded > ATH_EOA)
	farm->algo_def->max_excluded = f_cfg->max_excluded;
    if (f_cfg->expect_update > ATH_EOA)
	farm->expect_update = f_cfg->expect_update;
    if (engine->smartfarming == ATH_ON)
      {
	  if (f_cfg->smartfarmable == ATH_OFF)
	      farm->smartfarmable = ATH_OFF;
	  else
	      farm->smartfarmable = ATH_ON;
      }
    /* increment global farm counter */
    engine->farm_count++;
    return farm;
}

/* creates farm_cfg obj from AthFarm entry */
ath_cfg_farm * 
ath_farm_cfg_add(apr_pool_t *pool, ath_list *cfgs, const char *arg)
{
    const char *argend = (const char *)strchr(arg, '>');
    ath_cfg_farm *farm;
    int i;
    ASSERT(cfgs);
    ASSERT(arg);

    /* check if we got > in <AthenaFarm blah> */
    if (argend == NULL)
	return NULL;
    /* gets the name of the farm */
    arg = apr_pstrndup(pool, arg, argend - arg);
    arg = ath_ap_getword_conf(pool, &arg);
    if ((arg == NULL) || (strlen(arg) < 1))
	return NULL;
    /* checks for port (":") */
    i = 0;
    while (i < strlen(arg))
	if (*(arg + i++) == ':')
	    return NULL;

    farm = ath_farm_cfg_alloc(pool);
    ASSERT(farm);
    farm->name = apr_pstrndup(pool, arg, strlen(arg));
    ath_list_ap_add(pool, cfgs, farm);
    return farm;
}

/* adds a member to an existing farm cfg */
ath_cfg_phys * 
ath_farm_cfg_add_member(apr_pool_t *pool, ath_cfg_farm *farm, 
			ath_list *phys_cfgs, const char *args)
{
    int i;
    ath_cfg_phys *member_new;
    ath_cfg_phys *member;
    char *str_sep = " ";
    char *inter;
    char *arg1;
    char *arg2;
    char *myargs;
    int status;
    ASSERT(args);
    ASSERT(farm);

    /* check to see if name exist */
    if (strlen(args) < 1)
	return NULL;
  
    myargs = apr_pstrndup(pool, args, strlen(args));
    arg1 = apr_strtok(myargs, str_sep, &inter);

    /* check if we already have a phys_cfg for this one */
    if ((member = ath_phys_cfg_for_host(phys_cfgs, arg1)) != NULL)
      {
	  ath_list_ap_add(pool, farm->members, member);
	  /* phys online status determined by its first appearance
	   * in conf file
	   */
	  return member;
      }
    /* only alloc new cfg if not there */
    member_new = ath_phys_cfg_alloc(pool);
    member_new->host = apr_pstrndup(pool, arg1, strlen(arg1));

    arg2 = apr_strtok(NULL, str_sep, &inter);
    if (arg2)
      {
	  status = ath_flag_is_on(arg2);
	  member_new->on = status;
      }
    else
	member_new->on = ATH_EOA; /* will revert to default */

    /* ... and add it to total list phys_cfgs */
    ath_list_ap_add(pool, phys_cfgs, member_new);
    ath_list_ap_add(pool, farm->members, member_new);
    return member_new;
}

/* get a farm by its name 
 * return NULL if not found
 */
ath_farm * 
ath_farm_for_name(char *name)
{
    int i = 0;
    ath_farm *farm = NULL;
    ath_engine *engine;
    ASSERT(name);

    if (name)
      {
	  if (_cache)
	    {
		ath_farm_cache *cache = (ath_farm_cache *)
		  apr_hash_get(_cache, name, APR_HASH_KEY_STRING);
		if (cache)
		    farm = cache->farm;
	    }
	  else if (_cache_ppool)
	      ath_farm_cache_init(_cache_ppool);
	  
	  if (!farm)
	    {
		engine = ath_engine_get();
		while ((!farm) && (i < engine->farm_count))
		  {
		      farm = ath_farm_for_idx(i);
		      if ((!farm->name)
			  || (strlen(name) != strlen(farm->name)) 
			  || (strncmp(farm->name, name, strlen(farm->name))))
			  farm = NULL;
		      i++;
		  }
		if ((_cache) && (farm))
		  {
		      ath_farm_cache *cache = (ath_farm_cache *)
			apr_pcalloc(_cache_pool, 
				    sizeof(ath_farm_cache));
		      cache->farm = farm;
		      apr_hash_set(_cache, 
				   farm->name, APR_HASH_KEY_STRING, cache);
		  }
	    }
      }
    return farm;
}

ath_farm * 
ath_farm_for_idx(int idx)
{
    ath_farm *farm = ath_farm_raw_for_idx(idx);
    ASSERT(farm);
    if (farm->exists == 1)
	return farm;
    return NULL;
}

/* retrieves a valid farm slot in shm
 * doesn't care if farm was never configured 
 */
ath_farm * 
ath_farm_raw_for_idx(int idx)
{
    ath_farm *farm_base = ath_shm_farmbase();
    ath_farm *farm;
    ASSERT(-1 < idx < ATH_FARM_HARDMAX);
    farm = &farm_base[idx];
    return farm;
}

/* format a farm as an output string */
char * 
ath_farm_to_str(apr_pool_t *p, ath_farm *farm, const char *fmt)
{
    int i, c, idx;
    char *str_out = "";
    ath_phys *phys;
    ath_engine *engine = NULL;
    ASSERT(farm);
    ASSERT(p);

    /* this allows formatting for retrieved farm w/o module context */
    if (ath_shm_available())
	engine = ath_engine_get();

    while ((fmt) && (*fmt != (int) NULL))
      {
	  c = (int) *fmt;
	  if  (c == (int) ATH_CASE_ESC)
	    {
		char *addto;
		fmt++;
		c = (int) *fmt;
		switch ((char) c)
		  {
		  case ATH_CASE_F_NAME:
		      str_out = 
			apr_pstrcat(p, str_out, farm->name, NULL);
		      break;
		  case ATH_CASE_ON:
		      str_out = 
			apr_pstrcat(p, str_out, 
				    (apr_psprintf(p, "%d", farm->on)), NULL);
		      break;
		  case ATH_CASE_EXPECT:
		      str_out = 
			apr_pstrcat(p, str_out, 
				    (apr_psprintf(p, "%d", 
						  farm->expect_update)), NULL);
		      break;
		  case ATH_CASE_TTL:
		      str_out = 
			apr_pstrcat(p, str_out, 
				    (apr_psprintf(p, "%d", 
						  farm->allow_ttl)), NULL);
		      break;
		  case ATH_CASE_RTTL:
		      str_out = 
			apr_pstrcat(p, str_out, 
				    (apr_psprintf(p, "%d", 
						  farm->recover_ttl)), NULL);
		      break;
		  case ATH_CASE_CHOSEN:
		      if ((engine)
			  && (engine->nominate_per_update == 1))
			{
			    phys = ath_phys_for_idx(farm->member_phys_idx[farm->member_chosen_idx]);
			    if (phys)
				str_out = 
				  apr_pstrcat(p, str_out, phys->host, NULL);
			}
		      else
			  str_out = apr_pstrcat(p, str_out, "N/A", NULL);
		      break;
		  case ATH_CASE_DURL:
		      if (farm->down_url[0] != (int) NULL)
			  str_out = 
			    apr_pstrcat(p, str_out, farm->down_url, NULL);
		      else
			  str_out = 
			    apr_pstrcat(p, str_out, ATH_NULL_STR, NULL);
		      break;
		  case ATH_CASE_OURL:
		      if (farm->offline_url[0] != (int) NULL)
			  str_out = 
			    apr_pstrcat(p, str_out, farm->offline_url, NULL);
		      else
			  str_out = 
			    apr_pstrcat(p, str_out, ATH_NULL_STR, NULL);
		      break;
		  case ATH_CASE_SFABLE:
		      str_out = 
			apr_pstrcat(p, str_out,
				    (apr_psprintf(p, "%d", farm->smartfarmable)), NULL);
		      break;
		  case ATH_CASE_F_MEMB: /* farm memberships */
		      i = 0;
		      while (farm->member_phys_idx[i] != ATH_EOA)
			{
			    if (farm->member_phys_idx[i] != ATH_OPN)
			      {
				  idx = farm->member_phys_idx[i];
				  phys = ath_phys_for_idx(idx);
				  if (phys->exists)
				    {
					if (i == 0)
					    str_out = 
					      apr_pstrcat(p, str_out, 
							  phys->host, NULL);
					else
					    str_out = 
					      apr_pstrcat(p, str_out, " ", 
							  phys->host, NULL);
				    }
			      }
			    i++;
			}
		      break;
		  case ATH_CASE_ALGO:
		      str_out = 
			apr_pstrcat(p, str_out, 
				    (ath_algo_def_to_str(p, farm->algo_def)), 
				    NULL);
		      break;
		  case ATH_CASE_HITADDS: /* hit adds */
		      if (farm->hit_adds_to != ATH_ALGO_NON)
			{
			    str_out = 
			      apr_pstrcat(p, str_out, "adds ", 
					  (apr_psprintf(p, "%f", 
							farm->hit_adds)), 
					  " to ", 
					  (ath_algo_type_to_str(farm->hit_adds_to)),
					  NULL);
			}
		      break;
		  case ATH_CASE_SERIAL:
		      addto = ath_algo_type_to_str(farm->hit_adds_to);
		      str_out = 
			apr_pstrcat(p, str_out,
				    apr_psprintf(p, 
						 "%d|%d|%s|%s|%s|%s|%f"
						 "|%s|%d|%d|%d|%d|%d|%d|",
						 farm->idx,
						 farm->exists,
						 (*farm->name != (int)NULL) 
						 ? farm->name : ATH_NULL_STR,
						 (*farm->down_url != (int)NULL) 
						 ? farm->down_url : ATH_NULL_STR,
						 (*farm->offline_url != (int)NULL) 
						 ? farm->offline_url : ATH_NULL_STR,
						 ath_algo_def_to_str(p,farm->algo_def),
						 farm->hit_adds,
						 /*** FIX ME ***/
						 addto ? addto : ATH_NULL_STR,
						 farm->on,
						 farm->expect_update,
						 farm->allow_ttl,
						 farm->recover_ttl,
						 farm->member_chosen_idx,
						 farm->smartfarmable),
				    NULL);
		      /* for (i = 0; i < engine->phys_count - 1; i++) */
		      for (i = 0; (farm->member_phys_idx[i] != ATH_EOA); i++)
			{
			    str_out = 
			      apr_pstrcat(p, str_out,
					  apr_itoa(p, farm->member_phys_idx[i]), ",", 
					  NULL);
			}
		      i++;
		      str_out = 
			apr_pstrcat(p, str_out,
				    apr_itoa(p, farm->member_phys_idx[i]), NULL);
		      /* farm->lock_idx is not serialized */
		      break;
		  default:
		      str_out = apr_pstrcat(p, str_out, 
					    (apr_psprintf(p, "%%%c", (char) c)), NULL);
		  }
	    }
	  else
	    {
		str_out = apr_pstrcat(p, str_out, 
				      (apr_psprintf(p, "%c", (char) c)), NULL);
	    }
	  fmt++;
      }
    return str_out;
}

/* reverse the above. some parts must be handled by farm_safe.c */
ath_farm *
ath_farm_clone_from_str(apr_pool_t *pool, char *str_farm)
{
    char *str_tok1, *inter1;
    char *sep1 = "|";
    int i;
    ath_farm *farm_mirr;
    int farm_is_valid = 1;
    ASSERT(str_farm);
    /* order is:
     * idx|exists|farmname|downurl|algodef|hitadds
     *    |hitaddsto|on|expect_update|allow_ttl|recover_ttl
     *    |member_chosen_idx|phys1,phys2,...|smartfarmable|
     */
    farm_mirr = ath_farm_alloc(pool);
    if (strlen(str_farm) < 26)
	farm_is_valid = 0;
    i = 0;
    str_tok1 = apr_strtok(str_farm, sep1, &inter1);
    while ((str_tok1) && (farm_is_valid) && (i < 14))
      {
	  if (i==0)
	      farm_mirr->idx = atoi(str_tok1);
	  else if (i==1)
	      farm_mirr->exists = atoi(str_tok1);
	  else if (i==2)
	      ath_shm_strncpy(farm_mirr->name, str_tok1, strlen(str_tok1));
	  else if (i==3)
	    {
		if (!strncmp(str_tok1, ATH_NULL_STR, strlen(str_tok1)))
		    for (i = 0; i < ATH_NAME_MAX; i++)
			*farm_mirr->down_url = (int)NULL;
		else
		    ath_shm_strncpy(farm_mirr->down_url, str_tok1, 
				    strlen(str_tok1));
	    }
	  else if (i==4)
	    {
		if (!strncmp(str_tok1, ATH_NULL_STR, strlen(str_tok1)))
		    for (i = 0; i < ATH_NAME_MAX; i++)
			*farm_mirr->offline_url = (int)NULL;
		else
		    ath_shm_strncpy(farm_mirr->offline_url, 
				    str_tok1, strlen(str_tok1));
	    }
	  else if (i==5)
	      ath_algo_def_from_str(farm_mirr->algo_def, str_tok1);
	  else if (i==6)
	      farm_mirr->hit_adds = strtod(str_tok1, NULL);
	  else if (i==7)
	    {
		if (!strncmp(str_tok1, ATH_NULL_STR, strlen(str_tok1)))
		    farm_mirr->hit_adds_to = ATH_ALGO_NON;
		else
		    farm_mirr->hit_adds_to = ath_algo_str_to_type(str_tok1);
	    }
	  else if (i==8)
	      farm_mirr->on = atoi(str_tok1);
	  else if (i==9)
	      farm_mirr->expect_update = atoi(str_tok1);
	  else if (i==10)
	      farm_mirr->allow_ttl = atoi(str_tok1);
	  else if (i==11)
	      farm_mirr->recover_ttl = atoi(str_tok1);
	  else if (i==12)
	      farm_mirr->member_chosen_idx = atoi(str_tok1);
	  else if (i==13)
	    {
		char *str_tok2, *inter2;
		char *sep2 = ",";
		str_tok2 = apr_strtok(str_tok1, sep2, &inter2);
		if (str_tok2)
		  {
		      int j = 0;
		      while (str_tok2 != NULL)
			{
			    farm_mirr->member_phys_idx[j] = atoi(str_tok2);
			    j++;
			    str_tok2 = apr_strtok(NULL, sep2, &inter2);
			}
		  }
		else 
		    farm_is_valid = 0;
	    }
	  else if (i = 14)
	      farm_mirr->smartfarmable = atoi(str_tok1);
	  i++;
	  str_tok1 = apr_strtok(NULL, sep1, &inter1);
      }
    if ((i < 15) || (!farm_is_valid))
	farm_mirr = NULL;
    return farm_mirr;
}

/* adds an already configure phys to an already configed farm 
 * used by ath_update_farm
 */
void 
ath_farm_add_member(ath_farm *farm, ath_phys *phys)
{
    int idx = phys->idx;
    int j = 0;
    int g = 0;
    ASSERT(farm);
    ASSERT(phys);

    while (farm->member_phys_idx[j] != ATH_EOA)
      {
	  if (farm->member_phys_idx[j] == idx)
	      idx = ATH_EOA; /* already there, do not add */
	  if (farm->member_phys_idx[j] == ATH_OPN)
	      g = j; /* use this open slot */
	  j++;
      }
    if (idx != ATH_EOA) 
      {
	  if (g != 0) /* recycle spot */
	      farm->member_phys_idx[g] = idx;
	  else
	      /* j should be positioned at end of array */
	      farm->member_phys_idx[j] = idx;
	  /* also need to add farm in phys membership list */
	  idx = farm->idx;
	  j = 0;
	  g = 0;
	  while (phys->membership_idx[j] != ATH_EOA)
	    {
		if (phys->membership_idx[j] == idx)
		    idx = ATH_EOA; /* already there */
		if (phys->membership_idx[j] == ATH_OPN)
		    g = j;
		j++;
	    }
	  if (idx != ATH_EOA) 
	    {
		if (g != 0)
		    phys->membership_idx[g] = idx;
		else
		    phys->membership_idx[j] = idx;
	    }
      }
}

/* removes phys member from farm
 * both must already exist somewhere
 * slot in shm array is opened for recycle
 */ 
void 
ath_farm_remove_member(ath_farm *farm, ath_phys *phys)
{
    int idx = phys->idx;
    int i = 0;
    ASSERT(farm);
    ASSERT(phys);

    while ((farm->member_phys_idx[i] != ATH_EOA) && (idx != ATH_EOA))
      {
	  if (farm->member_phys_idx[i] == idx)
	    {
		farm->member_phys_idx[i] = ATH_OPN;
		idx = ATH_EOA; /* should not be there twice! */
	    }
	  i++;
      }
    i = 0;
    idx = farm->idx;
    while ((phys->membership_idx[i] != ATH_EOA) && (idx != ATH_EOA))
      {
	  if (phys->membership_idx[i] == idx)
	    {
		phys->membership_idx[i] = ATH_OPN;
		idx = ATH_EOA;
	    }
	  i++;
      }
}

ath_phys * 
ath_farm_member_for_idx(ath_farm *farm, int idx)
{
    int idx_p;
    ath_phys *phys = NULL;
    ASSERT(farm);
    if (idx > ATH_EOA)
      {
	  idx_p = farm->member_phys_idx[idx];
	  if (idx_p > ATH_EOA )
	      phys = ath_phys_for_idx(idx_p);
      }
    return phys;
}
